package fr.emse.tatiana.filter;

import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Path;

import fr.emse.tatiana.display.BadDisplayFile;
import fr.emse.tatiana.display.DisplayFileReader;

public class FilterExecuter {

	private String filterpath;
	private String resultpath;
	private String layoutpath;

	private Integer privatevarid = -1;

	private Vector<FilterAction> actions = new Vector<FilterAction>();
	private Vector<FilterParameter> parameters = new Vector<FilterParameter>();
	Map<Integer,FilterParameter> vars = new HashMap<Integer,FilterParameter>();

	// reads a tatiana filter and prepares its execution
	public FilterExecuter(String filterpath) throws BadDisplayFile {
		this.filterpath = filterpath;
		DisplayFileReader reader = new DisplayFileReader(filterpath);
		// TODO z later DisplayFileReader returns 0 when you try to read an integer that is not one
		this.parseParameters(reader);
		this.parseActions(reader);
		this.parseLayout(reader);
	}

	// these must be called in the correct order
	private void parseParameters(DisplayFileReader reader) throws BadDisplayFile {
		reader.selectFirstItem();
		while (reader.itemAvailable() && !reader.selectFirstInfo("filter")) {
			String label = reader.getStringValue("description", BadDisplayFile.MISSINGFIELD);
			Integer i = reader.getIntegerValue("result", BadDisplayFile.MISSINGFIELD);
			if (i<0) {
				throw new BadDisplayFile("filter file error: Negative result value");
			}
			String bydefault = null;
			String type = ftype;
			if (reader.selectFirstInfo("default")) {
				reader.updateInfoValue("String");
				bydefault = (String) reader.infoValue();
			}
			if (reader.selectFirstInfo("type")) {
				reader.updateInfoValue("String");
				type = (String) reader.infoValue();
			}
			FilterParameter fp = new FilterParameter(i,label,bydefault, type);
			parameters.add(fp);
			setVariableAt(i,fp);
			reader.selectNextItem();
		}
	}

	private void parseActions(DisplayFileReader reader) throws BadDisplayFile {
		boolean resultisset = false;
		while (reader.itemAvailable() && !resultisset) { // iterate over actions

			// get result, null if there is no result field
			Integer result = null;
			if (reader.selectFirstInfo("result")) {
				reader.updateInfoValue("Integer");
				result = (Integer) reader.infoValue();
				if (result<0) {
					throw new BadDisplayFile("filter file error: Negative result value");
				}
			} else {
				resultisset=true; // this is the last filter 
			}

			//read the filtername
			String filtername = reader.getStringValue("filter", BadDisplayFile.MISSINGFIELD);
			
			// read in each of the params
			Vector<Integer> params= new Vector<Integer>();
			reader.selectFirstInfo("param");
			while (reader.infoAvailable()) {
				String paramtype = reader.getInfoAttribute("paramtype");
				if ("value".equals(paramtype)) { // parameter is an actual value
					reader.updateInfoValue("String");
					params.add(addVariable((String) reader.infoValue()));
					System.err.println("got a value " + (String) reader.infoValue());
				} else { // "reference" or empty -> is a reference to the stored vars
					reader.updateInfoValue("Integer");
					Integer id = (Integer) reader.infoValue();
					if (id<0) {
						throw new BadDisplayFile("filter file error: Negative param value");
					}
					params.add(id);
				}
				reader.selectNextInfo("param");
			}
			
			// built this action
			actions.add(new FilterAction(filtername,result,params));
			reader.selectNextItem();
		}
	}

	private void parseLayout(DisplayFileReader reader) {
			try {
				layoutpath = reader.getStringValue("layout",BadDisplayFile.MISSINGFIELD);
			} catch (BadDisplayFile e) {
				// there is no layout
			}
	}

//	runs the filter
	public boolean execute(boolean nocache) {
		for (FilterParameter param : parameters) {
			String val = param.getValue();
			if (val == null) {
				return false; // filter cannot execute
			}
		}
		for (FilterAction action : actions) {
			action.execute(this, nocache); // TODO z later (what are they anyway?!) add cases where filter can't execute
		}
		return true;
	}

	public Vector<FilterParameter> getParameters() {
		return parameters;
	}
	
	public FilterParameter getParameterAt(Integer position) {
		return vars.get(position);
	}

	public void setVariableAt(Integer position, FilterParameter var) {
		if (position!=null) {
			vars.put(position, var);
		} else { // result of the filter
			IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(var.getValue()));
			this.resultpath = file.getLocation().toString();
		}
	}

	private Integer addVariable(String value) {
		Integer pos=privatevarid;
		FilterParameter fp = new FilterParameter(privatevarid,"",value,"String");
		setVariableAt(pos,fp);
		privatevarid--;
		return pos;
	}

//	returns a path to the result
	public String getResultPath() {
		return this.resultpath;
	}

//	returns a relative path to the layout file --- TODO deprecate this - doesn't belong in filters
	public String getLayoutPath() {
		return layoutpath;
	}
	
	
	public String getFilterPath() {
		return this.filterpath;
	}

	public static String ftype = "filename";
}
